use super::bsm::Option;
extern crate nalgebra as na;
use na::*;
use rand::thread_rng;
use rand_distr::{Distribution, Normal, NormalError};
use std::cmp;
extern crate docify;

#[doc = docify::embed!("src/option_price/binomial_tree.rs", test_bintree)]
impl Option {
    pub fn gen_bintree(&self, sigma: f64, steps: usize) -> (DMatrix<f64>, DMatrix<f64>) {
        let dt = self.T / steps as f64;
        let u = (sigma * dt.sqrt()).exp();
        let d = 1.0 / u;
        let p = (((self.r - self.divdend) * dt).exp() - d) / (u - d);

        let mut prices = na::DMatrix::<f64>::new_random(steps, steps);

        let mut options = na::DMatrix::<f64>::new_random(steps, steps);
        for i in 0..steps {
            for j in 0..i + 1 {
                let v = self.S * u.powf((i - j) as f64) * d.powf(j as f64);
                prices[(i, j)] = v;
                if self.is_call {
                    options[(i, j)] = if (v - self.K) > 0. { v - self.K } else { 0. };
                } else {
                    options[(i, j)] = if (self.K - v) > 0. { self.K - v } else { 0. };
                }
            }
        }
        (prices, options)
    }
    pub fn bintree_price(&self, sigma: f64, steps: usize) -> f64 {
        let dt = self.T / steps as f64;
        let u = (sigma * dt.sqrt()).exp();
        let d = 1.0 / u;
        let p = ((self.r * dt).exp() - d) / (u - d);
        let (prices, mut options) = self.gen_bintree(sigma, steps);
        for i in (0..steps - 1).rev() {
            for j in 0..i + 1 {
                options[(i, j)] = (p * options[(i + 1, j)] + (1. - p) * options[(i + 1, j + 1)])
                    * (-self.r * dt).exp();
            }
        }
        options[(0, 0)]
    }
}
#[test]
#[docify::export]
fn test_bintree() {
    let op = Option {
        is_call: true,
        S: 217.36, // base asset price
        K: 220.0,  // exacuate price
        T: 13.0 / 365.0,
        divdend: 0.44 / 100.0, //divdent rate
        r: 4.62 / 100.0,       //risk free rate
    };

    let sigma = 35.23 / 100.0;
    let steps = 100;
    let a = &op.bintree_price(sigma, steps);
    // dbg!("binomial tree==", a);
}
